package com.se.hospitalmonitoring;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import com.se.hospitalmonitoring.NurseStationControl.NurseStationObserver;
import com.se.hospitalmonitoring.VitalSensorInterface.MockVitalSensor;
import com.se.hospitalmonitoring.VitalSign.VitalSignMeasurement;

public class NurseStationInterface extends JFrame implements
	NurseStationObserver {

	private static final long serialVersionUID = 1L;
	private final JPanel contentPane;
	private final JPanel panel_1;

	private final HashMap<Long, HashMap<Long, JComponent[]>> bedMeasurementLabels = new HashMap<Long, HashMap<Long, JComponent[]>>();
	private final HashMap<Long, JPanel> bedPanels = new HashMap<Long, JPanel>();
	private final HashMap<Long, BlinkyThread> blinkyThreads = new HashMap<Long, BlinkyThread>();

	private static final SimpleDateFormat HIST_CONFIG_DATE_FORMAT = new SimpleDateFormat(
		"MM/dd/yyyy");

	/**
	 * Launch the GUI only.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					NurseStationInterface frame = new NurseStationInterface();
					frame.setVisible(true);
				}
				catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the frame.
	 */
	public NurseStationInterface() {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(100, 100, 800, 600);
		setTitle("Nurse Station");
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		setContentPane(contentPane);
		contentPane.setLayout(new BorderLayout(0, 0));

		panel_1 = new JPanel();
		contentPane.add(panel_1, BorderLayout.CENTER);
		panel_1.setLayout(new GridLayout(12, 1));
	}

	/**
	 * Called when an alarm is triggered
	 * 
	 * @param bedId
	 *            The bed that triggered the alarm
	 * @param alarmNotification
	 *            The alarm object
	 */
	@Override
	public void onNewAlarmNotification(long bedId,
		AlarmNotification alarmNotification) {
		if (bedPanels.get(bedId) == null
			|| BedAssignment.getInstance().getPatientByBed(bedId) == null)
			return;
		if (blinkyThreads.get(bedId) != null) {
			if (blinkyThreads.get(bedId).color.equals(Color.RED))
				return;
			blinkyThreads.get(bedId).stopGracefully();
		}
		BlinkyThread bt = new BlinkyThread(bedPanels.get(bedId), Color.RED);
		bt.start();
		blinkyThreads.put(bedId, bt);
	}

	/**
	 * Called when a call is triggered
	 * 
	 * @param bedId
	 *            The bed that triggered the alarm
	 * @param callNotification
	 *            The call object
	 */
	@Override
	public void onNewCallNotification(long bedId,
		CallNotification callNotification) {
		if (bedPanels.get(bedId) == null
			|| BedAssignment.getInstance().getPatientByBed(bedId) == null)
			return;
		if (blinkyThreads.get(bedId) == null) {
			BlinkyThread bt = new BlinkyThread(bedPanels.get(bedId),
				Color.YELLOW);
			bt.start();
			blinkyThreads.put(bedId, bt);
		}
	}

	/**
	 * Called when a new vital sign measurement is received
	 * 
	 * @param bedId
	 *            The bed
	 * @param vitalSign
	 *            The vital sign that was received
	 */
	@Override
	public void onNewVitalSign(long bedId, VitalSignMeasurement vitalSign) {
		if (bedMeasurementLabels.get(bedId) != null
			&& bedMeasurementLabels.get(bedId).get(vitalSign.vitalSign.id) != null) {
			JComponent[] components = bedMeasurementLabels.get(bedId).get(
				vitalSign.vitalSign.id);
			((JLabel) components[0]).setText(vitalSign.toString());
			BedAssignment.getInstance().getPatientByBed(bedId).history
				.addVitalSignMeasurement(vitalSign);
		}
	}

	/**
	 * Called when a new bed starts up
	 * 
	 * @param bedId
	 *            The id of the bed
	 */
	@Override
	public void onNewBed(final long bedId) {
		final JPanel newBedPanel = new JPanel();
		bedPanels.put(bedId, newBedPanel);
		newBedPanel.setLayout(new GridLayout(1, 6, 5, 5));

		final JLabel unassignedLabel = new JLabel("Bed #" + bedId
			+ " unassigned");
		newBedPanel.add(unassignedLabel);
		final JButton admitButton = new JButton("Assign Patient");
		admitButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				final JDialog admitDialog = new JDialog(
					NurseStationInterface.this);
				admitDialog.setSize(400, 200);
				admitDialog.setLocationRelativeTo(admitDialog);
				admitDialog.setTitle("Admit new patient");
				JPanel contentPanel = new JPanel(new BorderLayout());
				JPanel fieldsPanel = new JPanel(new GridLayout(2, 1));
				JPanel nameRow = new JPanel(new FlowLayout(FlowLayout.CENTER));
				nameRow.add(new JLabel("Patient Name:"));
				final JTextField nameField = new JTextField();
				nameField.setColumns(25);
				nameRow.add(nameField);
				JPanel ageRow = new JPanel(new FlowLayout(FlowLayout.CENTER));
				ageRow.add(new JLabel("Age:"));
				final JTextField ageField = new JTextField();
				ageField.setColumns(3);
				ageRow.add(ageField);
				fieldsPanel.add(nameRow);
				fieldsPanel.add(ageRow);
				contentPanel.add(fieldsPanel, BorderLayout.CENTER);
				JPanel buttons = new JPanel(new FlowLayout());
				JButton okButton = new JButton("OK");
				okButton.addActionListener(new ActionListener() {

					@Override
					public void actionPerformed(ActionEvent e) {
						String name = nameField.getText();
						int age = -1;
						try {
							age = Integer.parseInt(ageField.getText());
						}
						catch (Exception e2) {
							admitDialog.dispose();
						}
						if (name == null || name.equals("") || age == -1)
							admitDialog.dispose();

						PatientRecord pr = new PatientRecord(BedAssignment
							.generatePatientId(), name, age);
						BedAssignment.getInstance().assignBed(bedId, pr);
						newBedPanel.removeAll();
						newBedPanel
							.add(new JLabel("Bed #" + bedId + " " + name));

						bedMeasurementLabels.put(bedId,
							new HashMap<Long, JComponent[]>());
						for (final VitalSign vs : MockVitalSensor.VITAL_SIGNS) {
							JPanel measurementPanel = new JPanel();
							measurementPanel.setOpaque(false);
							measurementPanel.setBorder(LineBorder
								.createBlackLineBorder());
							measurementPanel.setLayout(new GridLayout(2, 1, 0,
								0));

							JLabel title = new JLabel(vs.name);
							title.setFont(new Font("measureTitle", Font.PLAIN,
								16));

							measurementPanel.add(title);

							JLabel heartRateValueLabel = new JLabel();
							heartRateValueLabel.setFont(new Font(
								"measureValue", Font.PLAIN, 24));
							measurementPanel.add(heartRateValueLabel);
							newBedPanel.add(measurementPanel);

							bedMeasurementLabels.get(bedId).put(vs.id,
								new JComponent[] { heartRateValueLabel });
						}

						JButton history = new JButton("View History");
						history.addActionListener(new ActionListener() {

							@Override
							public void actionPerformed(ActionEvent e) {
								final JDialog historyConfig = new JDialog(
									NurseStationInterface.this);
								JPanel contentPanel = new JPanel(
									new GridLayout(5, 1));
								contentPanel.setBorder(new EmptyBorder(10, 10,
									10, 10));
								contentPanel
									.add(new JLabel(
										"Choose the time span to view history for."));
								JPanel fieldsPanel = new JPanel(new FlowLayout(
									FlowLayout.CENTER, 15, 5));
								fieldsPanel.add(new JLabel(
									"Start (mm/dd/yyyy):"));
								final JTextField startDate = new JTextField();
								JPanel fieldsPanel2 = new JPanel(
									new FlowLayout(FlowLayout.CENTER, 15, 5));
								startDate.setColumns(10);
								fieldsPanel.add(startDate);
								fieldsPanel2
									.add(new JLabel("End (mm/dd/yyyy):"));
								final JTextField endDate = new JTextField();
								endDate.setColumns(10);
								fieldsPanel2.add(endDate);
								contentPanel.add(fieldsPanel);
								contentPanel.add(fieldsPanel2);
								final JCheckBox allHistory = new JCheckBox(
									"Show all history");
								allHistory
									.addChangeListener(new ChangeListener() {

										@Override
										public void stateChanged(ChangeEvent e) {
											boolean selected = allHistory
												.isSelected();
											startDate.setEnabled(!selected);
											endDate.setEnabled(!selected);
										}
									});
								contentPanel.add(allHistory);
								JPanel buttons = new JPanel(new FlowLayout());
								JButton okButton = new JButton("OK");
								JButton cancelButton = new JButton("Cancel");
								okButton
									.addActionListener(new ActionListener() {

										@Override
										public void actionPerformed(
											ActionEvent e) {
											if (!allHistory.isSelected()) {
												Date start = null;
												Date end = null;
												try {
													start = HIST_CONFIG_DATE_FORMAT
														.parse(startDate
															.getText());
													end = HIST_CONFIG_DATE_FORMAT
														.parse(endDate
															.getText());
												}
												catch (ParseException e1) {
													// Holy cow this method is
													// long
													new JOptionPane(
														"Error parsing a date",
														JOptionPane.ERROR_MESSAGE)
														.setVisible(true);
													return;
												}
												new PatientHistoryInterface(
													BedAssignment.getInstance()
														.getPatientByBed(bedId),
													start.getTime(), end
														.getTime())
													.setVisible(true);
											}
											else {
												new PatientHistoryInterface(
													BedAssignment.getInstance()
														.getPatientByBed(bedId),
													PatientHistoryInterface.ALL_HISTORY,
													PatientHistoryInterface.ALL_HISTORY)
													.setVisible(true);
											}
											historyConfig.dispose();
										}
									});
								cancelButton
									.addActionListener(new ActionListener() {

										@Override
										public void actionPerformed(
											ActionEvent e) {
											historyConfig.dispose();
										}
									});
								buttons.add(okButton);
								buttons.add(cancelButton);
								contentPanel.add(buttons);
								historyConfig.setSize(400, 250);
								historyConfig
									.setLocationRelativeTo(NurseStationInterface.this);
								historyConfig.add(contentPanel);
								historyConfig.setVisible(true);
							}
						});
						newBedPanel.add(history);
						JButton unassign = new JButton("Discharge");
						unassign.addActionListener(new ActionListener() {

							@Override
							public void actionPerformed(ActionEvent e) {
								if (blinkyThreads.get(bedId) != null)
									blinkyThreads.get(bedId).stopGracefully();
								newBedPanel.removeAll();
								newBedPanel.add(unassignedLabel);
								newBedPanel.add(admitButton);
								newBedPanel.revalidate();
								newBedPanel.repaint();
							}
						});
						newBedPanel.add(unassign);
						newBedPanel.revalidate();
						newBedPanel.repaint();
						admitDialog.dispose();
					}
				});
				JButton cancelButton = new JButton("Cancel");
				cancelButton.addActionListener(new ActionListener() {

					@Override
					public void actionPerformed(ActionEvent e) {
						admitDialog.dispose();
					}
				});
				buttons.add(okButton);
				buttons.add(cancelButton);
				contentPanel.add(buttons, BorderLayout.SOUTH);
				admitDialog.add(contentPanel);
				admitDialog.setVisible(true);
			}
		});
		newBedPanel.add(admitButton);
		panel_1.add(newBedPanel);
		panel_1.revalidate();
		panel_1.repaint();
	}

	private class BlinkyThread extends Thread {
		private boolean stop = false;

		private JPanel panel = null;
		private Color color = null;
		private Color color2 = null;

		public BlinkyThread(JPanel jPanel, Color color) {
			panel = jPanel;
			this.color = color;
			color2 = Color.LIGHT_GRAY;
		}

		public void stopGracefully() {
			stop = true;
		}

		@Override
		public void run() {
			while (!stop) {
				if (panel.getBackground() == color) {
					panel.setBackground(color2);
				}
				else {
					panel.setBackground(color);
				}
				try {
					Thread.sleep(1000);
				}
				catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			panel.setBackground(color2);
		}
	}

	/**
	 * Called when an alarm is reset
	 * 
	 * @param bedId
	 *            The bed
	 */
	@Override
	public void onAlarmCanceled(long bedId) {
		if (blinkyThreads.get(bedId) != null) {
			blinkyThreads.get(bedId).stopGracefully();
			blinkyThreads.put(bedId, null);
		}
	}

	/**
	 * Called when a call button is reset
	 * 
	 * @param bedId
	 *            The bed
	 */
	@Override
	public void onCallCanceled(long bedId) {
		if (blinkyThreads.get(bedId) != null
			&& blinkyThreads.get(bedId).color.equals(Color.YELLOW)) {
			blinkyThreads.get(bedId).stopGracefully();
			blinkyThreads.put(bedId, null);
		}
	}
}
